/*!
 * Ext JS Library 3.1.0
 * Copyright(c) 2006-2009 Ext JS, LLC
 * licensing@extjs.com
 * http://www.extjs.com/license
 */
/**
 * @class Ext.ux.tree.TreeGrid
 * @extends Ext.tree.TreePanel
 *
 * @xtype treegrid
 */
Ext.ux.tree.TreeGrid = Ext.extend(Ext.tree.TreePanel, {
    rootVisible: false,
    useArrows: true,
    lines: false,
    borderWidth: Ext.isBorderBox ? 0 : 2, // the combined left/right border for each cell
    cls: 'x-treegrid',

    columnResize: true,
    enableSort: true,
    reserveScrollOffset: true,
    enableHdMenu: true,

    columnsText: 'Columns',

    initComponent: function () {
        if (!this.root) {
            this.root = new Ext.tree.AsyncTreeNode({text: 'Root'});
        }

        // initialize the loader
        var l = this.loader;
        if (!l) {
            l = new Ext.ux.tree.TreeGridLoader({
                dataUrl: this.dataUrl,
                requestMethod: this.requestMethod,
                store: this.store
            });
        } else if (Ext.isObject(l) && !l.load) {
            l = new Ext.ux.tree.TreeGridLoader(l);
        } else if (l) {
            l.createNode = function (attr) {
                if (!attr.uiProvider) {
                    attr.uiProvider = Ext.ux.tree.TreeGridNodeUI;
                }
                return Ext.tree.TreeLoader.prototype.createNode.call(this, attr);
            }
        }
        this.loader = l;

        Ext.ux.tree.TreeGrid.superclass.initComponent.call(this);

        this.initColumns();

        if (this.enableSort) {
            this.treeGridSorter = new Ext.ux.tree.TreeGridSorter(this, this.enableSort);
        }

        if (this.columnResize) {
            this.colResizer = new Ext.tree.ColumnResizer(this.columnResize);
            this.colResizer.init(this);
        }

        var c = this.columns;
        if (!this.internalTpl) {
            this.internalTpl = new Ext.XTemplate(
                '<div class="x-grid3-header">',
                '<div class="x-treegrid-header-inner">',
                '<div class="x-grid3-header-offset">',
                '<table cellspacing="0" cellpadding="0" border="0"><colgroup><tpl for="columns"><col /></tpl></colgroup>',
                '<thead><tr class="x-grid3-hd-row">',
                '<tpl for="columns">',
                '<td class="x-grid3-hd x-grid3-cell x-treegrid-hd" style="text-align: {align};" id="', this.id, '-xlhd-{#}">',
                '<div class="x-grid3-hd-inner x-treegrid-hd-inner" unselectable="on">',
                this.enableHdMenu ? '<a class="x-grid3-hd-btn" href="#"></a>' : '',
                '{header}<img class="x-grid3-sort-icon" src="', Ext.BLANK_IMAGE_URL, '" />',
                '</div>',
                '</td></tpl>',
                '</tr></thead>',
                '</div></table>',
                '</div></div>',
                '</div>',
                '<div class="x-treegrid-root-node">',
                '<table class="x-treegrid-root-table" cellpadding="0" cellspacing="0" style="table-layout: fixed;"></table>',
                '</div>'
            );
        }

        if (!this.colgroupTpl) {
            this.colgroupTpl = new Ext.XTemplate(
                '<colgroup><tpl for="columns"><col style="width: {width}px"/></tpl></colgroup>'
            );
        }
    },

    initColumns: function () {
        var cs = this.columns,
            len = cs.length,
            columns = [],
            i, c;

        for (i = 0; i < len; i++) {
            c = cs[i];
            if (!c.isColumn) {
                c.xtype = c.xtype ? (/^tg/.test(c.xtype) ? c.xtype : 'tg' + c.xtype) : 'tgcolumn';
                c = Ext.create(c);
            }
            c.init(this);
            columns.push(c);

            if (this.enableSort !== false && c.sortable !== false) {
                c.sortable = true;
                this.enableSort = true;
            }
        }

        this.columns = columns;
    },

    onRender: function () {
        Ext.tree.TreePanel.superclass.onRender.apply(this, arguments);

        this.el.addClass('x-treegrid');

        this.outerCt = this.body.createChild({
            cls: 'x-tree-root-ct x-treegrid-ct ' + (this.useArrows ? 'x-tree-arrows' : this.lines ? 'x-tree-lines' : 'x-tree-no-lines')
        });

        this.internalTpl.overwrite(this.outerCt, {columns: this.columns});

        this.mainHd = Ext.get(this.outerCt.dom.firstChild);
        this.innerHd = Ext.get(this.mainHd.dom.firstChild);
        this.innerBody = Ext.get(this.outerCt.dom.lastChild);
        this.innerCt = Ext.get(this.innerBody.dom.firstChild);

        this.colgroupTpl.insertFirst(this.innerCt, {columns: this.columns});

        if (this.hideHeaders) {
            this.header.dom.style.display = 'none';
        } else if (this.enableHdMenu !== false) {
            this.hmenu = new Ext.menu.Menu({id: this.id + '-hctx'});
            if (this.enableColumnHide !== false) {
                this.colMenu = new Ext.menu.Menu({id: this.id + '-hcols-menu'});
                this.colMenu.on({
                    scope: this,
                    beforeshow: this.beforeColMenuShow,
                    itemclick: this.handleHdMenuClick
                });
                this.hmenu.add({
                    itemId: 'columns',
                    hideOnClick: false,
                    text: this.columnsText,
                    menu: this.colMenu,
                    iconCls: 'x-cols-icon'
                });
            }
            this.hmenu.on('itemclick', this.handleHdMenuClick, this);
        }
    },

    setRootNode: function (node) {
        node.attributes.uiProvider = Ext.ux.tree.TreeGridRootNodeUI;
        node = Ext.ux.tree.TreeGrid.superclass.setRootNode.call(this, node);
        if (this.innerCt) {
            this.colgroupTpl.insertFirst(this.innerCt, {columns: this.columns});
        }
        return node;
    },

    initEvents: function () {
        Ext.ux.tree.TreeGrid.superclass.initEvents.apply(this, arguments);

        this.mon(this.innerBody, 'scroll', this.syncScroll, this);
        this.mon(this.innerHd, 'click', this.handleHdDown, this);
        this.mon(this.mainHd, {
            scope: this,
            mouseover: this.handleHdOver,
            mouseout: this.handleHdOut
        });
    },

    onResize: function (w, h) {
        Ext.ux.tree.TreeGrid.superclass.onResize.apply(this, arguments);

        var bd = this.innerBody.dom;
        var hd = this.innerHd.dom;

        if (!bd) {
            return;
        }

        if (Ext.isNumber(h)) {
            bd.style.height = this.body.getHeight(true) - hd.offsetHeight + 'px';
        }

        if (Ext.isNumber(w)) {
            var sw = Ext.num(this.scrollOffset, Ext.getScrollBarWidth());
            if (this.reserveScrollOffset || ((bd.offsetWidth - bd.clientWidth) > 10)) {
                this.setScrollOffset(sw);
            } else {
                var me = this;
                setTimeout(function () {
                    me.setScrollOffset(bd.offsetWidth - bd.clientWidth > 10 ? sw : 0);
                }, 10);
            }
        }
    },

    updateColumnWidths: function () {
        var cols = this.columns,
            colCount = cols.length,
            groups = this.outerCt.query('colgroup'),
            groupCount = groups.length,
            c, g, i, j;

        for (i = 0; i < colCount; i++) {
            c = cols[i];
            for (j = 0; j < groupCount; j++) {
                g = groups[j];
                g.childNodes[i].style.width = (c.hidden ? 0 : c.width) + 'px';
            }
        }

        for (i = 0, groups = this.innerHd.query('td'), len = groups.length; i < len; i++) {
            c = Ext.fly(groups[i]);
            if (cols[i] && cols[i].hidden) {
                c.addClass('x-treegrid-hd-hidden');
            } else {
                c.removeClass('x-treegrid-hd-hidden');
            }
        }

        var tcw = this.getTotalColumnWidth();
        Ext.fly(this.innerHd.dom.firstChild).setWidth(tcw + (this.scrollOffset || 0));
        this.outerCt.select('table').setWidth(tcw);
        this.syncHeaderScroll();
    },

    getVisibleColumns: function () {
        var columns = [],
            cs = this.columns,
            len = cs.length,
            i;

        for (i = 0; i < len; i++) {
            if (!cs[i].hidden) {
                columns.push(cs[i]);
            }
        }
        return columns;
    },

    getTotalColumnWidth: function () {
        var total = 0;
        for (var i = 0, cs = this.getVisibleColumns(), len = cs.length; i < len; i++) {
            total += cs[i].width;
        }
        return total;
    },

    setScrollOffset: function (scrollOffset) {
        this.scrollOffset = scrollOffset;
        this.updateColumnWidths();
    },

    // private
    handleHdDown: function (e, t) {
        var hd = e.getTarget('.x-treegrid-hd');

        if (hd && Ext.fly(t).hasClass('x-grid3-hd-btn')) {
            var ms = this.hmenu.items,
                cs = this.columns,
                index = this.findHeaderIndex(hd),
                c = cs[index],
                sort = c.sortable;

            e.stopEvent();
            Ext.fly(hd).addClass('x-grid3-hd-menu-open');
            this.hdCtxIndex = index;

            this.fireEvent('headerbuttonclick', ms, c, hd, index);

            this.hmenu.on('hide', function () {
                Ext.fly(hd).removeClass('x-grid3-hd-menu-open');
            }, this, {single: true});

            this.hmenu.show(t, 'tl-bl?');
        } else if (hd) {
            var index = this.findHeaderIndex(hd);
            this.fireEvent('headerclick', this.columns[index], hd, index);
        }
    },

    // private
    handleHdOver: function (e, t) {
        var hd = e.getTarget('.x-treegrid-hd');
        if (hd && !this.headersDisabled) {
            index = this.findHeaderIndex(hd);
            this.activeHdRef = t;
            this.activeHdIndex = index;
            var el = Ext.get(hd);
            this.activeHdRegion = el.getRegion();
            el.addClass('x-grid3-hd-over');
            this.activeHdBtn = el.child('.x-grid3-hd-btn');
            if (this.activeHdBtn) {
                this.activeHdBtn.dom.style.height = (hd.firstChild.offsetHeight - 1) + 'px';
            }
        }
    },

    // private
    handleHdOut: function (e, t) {
        var hd = e.getTarget('.x-treegrid-hd');
        if (hd && (!Ext.isIE || !e.within(hd, true))) {
            this.activeHdRef = null;
            Ext.fly(hd).removeClass('x-grid3-hd-over');
            hd.style.cursor = '';
        }
    },

    findHeaderIndex: function (hd) {
        hd = hd.dom || hd;
        var cs = hd.parentNode.childNodes;
        for (var i = 0, c; c = cs[i]; i++) {
            if (c == hd) {
                return i;
            }
        }
        return -1;
    },

    // private
    beforeColMenuShow: function () {
        var cols = this.columns,
            colCount = cols.length,
            i, c;
        this.colMenu.removeAll();
        for (i = 1; i < colCount; i++) {
            c = cols[i];
            if (c.hideable !== false) {
                this.colMenu.add(new Ext.menu.CheckItem({
                    itemId: 'col-' + i,
                    text: c.header,
                    checked: !c.hidden,
                    hideOnClick: false,
                    disabled: c.hideable === false
                }));
            }
        }
    },

    // private
    handleHdMenuClick: function (item) {
        var index = this.hdCtxIndex,
            id = item.getItemId();

        if (this.fireEvent('headermenuclick', this.columns[index], id, index) !== false) {
            index = id.substr(4);
            if (index > 0 && this.columns[index]) {
                this.setColumnVisible(index, !item.checked);
            }
        }

        return true;
    },

    setColumnVisible: function (index, visible) {
        this.columns[index].hidden = !visible;
        this.updateColumnWidths();
    },

    /**
     * Scrolls the grid to the top
     */
    scrollToTop: function () {
        this.innerBody.dom.scrollTop = 0;
        this.innerBody.dom.scrollLeft = 0;
    },

    // private
    syncScroll: function () {
        this.syncHeaderScroll();
        var mb = this.innerBody.dom;
        this.fireEvent('bodyscroll', mb.scrollLeft, mb.scrollTop);
    },

    // private
    syncHeaderScroll: function () {
        var mb = this.innerBody.dom;
        this.innerHd.dom.scrollLeft = mb.scrollLeft;
        this.innerHd.dom.scrollLeft = mb.scrollLeft; // second time for IE (1/2 time first fails, other browsers ignore)
    },

    registerNode: function (n) {
        Ext.ux.tree.TreeGrid.superclass.registerNode.call(this, n);
        if (!n.uiProvider && !n.isRoot && !n.ui.isTreeGridNodeUI) {
            n.ui = new Ext.ux.tree.TreeGridNodeUI(n);
        }
    }
});

Ext.reg('treegrid', Ext.ux.tree.TreeGrid);